#pragma once

#include <iostream>
#include <string>
#include <assert.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <netinet/in.h>

class Sock
{
    // listen的第二个参数，意义：底层全连接队列的长度 = listen的第二个参数+1
    const static int gbacklog = 10;

public:
    static int Socket()
    {
        // 创建套接字：
        // int socket(int domain, int type, int protocol);
        int listensock = socket(AF_INET, SOCK_STREAM, 0);
        if (listensock < 0)
            exit(2);

        // 为了防止服务端断开后无法立即重启：
        // int getsockopt(int sockfd, int level, int optname,void *optval, socklen_t *optlen);
        int opt = 1;
        setsockopt(listensock, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));
        return listensock;
    }

    static void Bind(int sock, uint16_t port, std::string ip = "0.0.0.0")
    {
        // 绑定套接字：
        // int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
        struct sockaddr_in local;
        bzero(&local, sizeof local); // 清零
        local.sin_family = AF_INET;
        local.sin_port = htons(port);           // 主机字节序-->网络字节序
        inet_aton(ip.c_str(), &local.sin_addr); // 主机字节序+点分十进制--->网络字节序+四字节序

        if (bind(sock, (const sockaddr *)&local, sizeof(local)) < 0)
            exit(3);
    }

    static void Listen(int sock)
    {
        // 监听: int listen(int sockfd, int backlog);
        if (listen(sock, gbacklog) < 0)
            exit(4);
    }

    static int Accept(int sock, uint16_t *port, std::string *ip, int* accept_errno)
    {
        // 获取连接：
        // int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
        struct sockaddr_in client;
        bzero(&client, sizeof(client));
        socklen_t len = sizeof(client);
        int servicesock = accept(sock, (struct sockaddr *)&client, &len);
        if (servicesock < 0)
        {
            *accept_errno = errno;// 获取错误码
            return -1;
        }

        // 将获取到的客户端端口号和ip返回给服务器（这里是通过输出型参数的方式）
        if (port)
            *port = ntohs(client.sin_port); // 网络字节序-->主机字节序
        if (ip)
            *ip = inet_ntoa(client.sin_addr);

        return servicesock;
    }

    static bool Connect(int sock, const uint16_t &port, const std::string &ip)
    {
        // int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
        struct sockaddr_in server;
        bzero(&server, sizeof(server));
        server.sin_family = AF_INET;
        server.sin_port = htons(port);
        server.sin_addr.s_addr = inet_addr(ip.c_str());

        if (connect(sock, (struct sockaddr *)&server, sizeof server) < 0)
            return false;
        else
            return true;
    }

    static bool SetNonBlock(int sock)
    {
        int fl = fcntl(sock, F_GETFL);
        if(fl < 0)//On error, -1 is returned, and errno is set appropriately.
            return false;
        if(fcntl( sock, F_SETFL, fl | O_NONBLOCK) < 0) return false;
        return true;
    }
};